#define FNT_C

#include <kos.h>
#include <stdlib.h>
#include <dc/fmath.h>
#include <png/png.h>

#include "utils.h"
#include "font.h"

fnt_obj_t fnt_objs[FNT_MAX_OBJECTS];      /* static array of all font objects */

fnt_info_t *fnt_first = 0;           /* first font loaded, used for 'objects' */

/* fnt_init _________________________________________________________________ */
/*                                                                            */
/* Initialise font support.                                                   */
/* __________________________________________________________________________ */

void fnt_init(void)
{
  DBG_ENTRY(99,("fnt_init()"));
  
  memset(fnt_objs, 0, sizeof(fnt_obj_t)*FNT_MAX_OBJECTS);

  fnt_first = 0;

  DBG_EXIT(99,("fnt_init"));
  
  return;
}

/* fnt_term _________________________________________________________________ */
/*                                                                            */
/* Terminate font support.                                                    */
/* __________________________________________________________________________ */

void fnt_term(void)
{
  DBG_ENTRY(99,("fnt_term()"));
  
  DBG_EXIT(99,("fnt_term"));
  
  return;
}

/* fnt_write_simple _________________________________________________________ */
/*                                                                            */
/* Write a simple string (with a border) to the screen                        */
/* __________________________________________________________________________ */

void fnt_write_simple(fnt_info_t *font, char *string, 
                      int x, int y, float z, float scale, float fade, uint32 colour)
{
  fnt_write(font,
            string,
            x, y, 0,
            z,
            0,
            scale,
            fade,
            colour,
            0, 0,
            4,
            1,
            0x000000);
}

/* fnt_write ________________________________________________________________ */
/*                                                                            */
/* Generate text using given font (optionally, NOT to screen).                */
/* __________________________________________________________________________ */

void fnt_write(fnt_info_t *font, char *string, 
               int x, int y, int max_x, float z, int rotation,
               float scale, float fade, uint32 colour,
               int *wid_out, int *hgt_out, int8 display,
               int outline_wid, uint32 outline_colour)
{
  int   fc = 0;
  int   mid_x = 0, mid_y = 0;
  int   n, cur_x, cur_y, wid, max_wid, hgt, max_hgt;
  float u1, u2, rcos = 0, rsin = 0;
  int   outline = outline_wid * (int)scale;
  int   fixed_space = 0;
  
  DBG_ENTRY(99,("fnt_write(%p,%p,%f,...)",font,string,(double)scale));
  
  /* ------------------------ */
  /* allow for auto centering */
  /* ------------------------ */
  if (display > 0 && (x == FNT_CENTRAL || y == FNT_CENTRAL))
  {
    display = -display;
    fc = 1;
  }

  rotation += view_rot;

  if (rotation)
  {
    rcos = fcos((float)rotation * UTL_DEG2RAD);
    rsin = fsin((float)rotation * UTL_DEG2RAD);
  }

  REDO:

  n       = 0;
  cur_x   = x;
  cur_y   = y;
  max_wid = 0;
  hgt     = (int)((float)font->height*scale);
  max_hgt = hgt;

  while(string[n])
  {
    switch(string[n])
    {
      case '\n':
        max_wid = UTL_MAX((cur_x-x),max_wid);
        cur_x   =  x;
        cur_y   += max_hgt;
        max_hgt = hgt;
        break;

      case '\1':            /* size inc */
        if (string[n+1])
        {
          scale = scale * ((float)string[++n]);

          if ((hgt  = (int)((float)font->height*scale)) > max_hgt)
            max_hgt = hgt;
        }
        break;

      case '\2':            /* size dec */
        if (string[n+1])
        {
          if ((scale = scale / ((float)string[++n])) < 0)
            scale = 0.0;

          hgt = (int)((float)font->height*scale);
        }
        break;

      case '\3':            /* x inc */
        if (string[n+1])
        {
          int delta = (int)(((float)string[++n])*scale);

          if (!max_x || (cur_x + delta < max_x))
            cur_x += delta;
        }
        break;

      case '\4':            /* x dec */
        if (string[n+1])
          cur_x -= (int)(((float)string[++n])*scale);;
        break;

      case '\5':            /* y inc */
        if (string[n+1])
          cur_y += (int)(((float)string[++n])*scale);
        break;

      case '\6':            /* y dec */
        if (string[n+1])
          cur_y -= (int)(((float)string[++n])*scale);
        break;

      case '\7':            /* fixed space mode */
        fixed_space = (int)string[++n];
        break;
        
      default:
        {
          int bef = font->pos_array[(int)string[n]-1];
          int aft = font->pos_array[(int)string[n]];

          if (fixed_space == 0)
            wid = (int)((float)(aft-bef)*scale);
          else
            wid = fixed_space;
          
          if (wid>0)
          {
            if (aft>(int)font->txr_width)
            {
              int txr_start = font->txr_width * (aft/font->txr_width);

              if (bef<txr_start)
                bef = 0;
              else
                bef -= txr_start;

              aft -= txr_start;

              wid = (int)((float)(aft-bef)*scale);
            }

            /* --------------------------------- */
            /* we've written the maximum allowed */
            /* --------------------------------- */

            if (max_x && (cur_x + wid > max_x))
            {
              max_wid = UTL_MAX((cur_x-x),max_wid);
              cur_x   =  x;
              cur_y   += max_hgt;
              max_hgt = hgt;
            }

            if (display>0)
            {
              int x1, y1, x2, y2, x3, y3, x4, y4;
              int xd = 0, yd = 0;

              int ic = colour,loop;
              float iz = z;

              pvr_vertex_t vert; 

              u1 = (float)bef / (float)font->txr_width;
              u2 = (float)aft / (float)font->txr_width;

              switch(display)
              {
                case 3:
                  loop = 3;
                  break;
                case 4:
                  loop = 0;
                  break;
                default:
                  loop = 4;
                  break;
              }

              x1 = x3 = cur_x;
              y1 = y2 = cur_y;
              x2 = x4 = x1+wid;
              y3 = y4 = y1+hgt;

              /* ------------------ */
              /* Calculate rotation */
              /* ------------------ */
              if (rotation != 0)
              {
                int nx, ny;

                nx = (int)(rcos*x1+rsin*y1);
                ny = (int)(rsin*-x1+rcos*y1);
                x1=nx; y1=ny;
                nx = (int)(rcos*x2+rsin*y2);
                ny = (int)(rsin*-x2+rcos*y2);
                x2=nx; y2=ny;
                nx = (int)(rcos*x3+rsin*y3);
                ny = (int)(rsin*-x3+rcos*y3);
                x3=nx; y3=ny;
                nx = (int)(rcos*x4+rsin*y4);
                ny = (int)(rsin*-x4+rcos*y4);
                x4=nx; y4=ny;
              }

              if (mid_x)
              {
                x1+=mid_x;
                y1+=mid_y;
                x2+=mid_x;
                y2+=mid_y;
                x3+=mid_x;
                y3+=mid_y;
                x4+=mid_x;
                y4+=mid_y;
              }

              if (view_scale != 1)
              {
                x1 *= view_scale;
                x2 *= view_scale;
                x3 *= view_scale;
                x4 *= view_scale;
                y1 *= view_scale;
                y2 *= view_scale;
                y3 *= view_scale;
                y4 *= view_scale;
              }

              while(loop++<5)
              {
                switch(loop)
                {
                  case 1:
                    xd = yd = -outline;
                    iz = z-0.1f;
                    ic = outline_colour;
                    break;
                  case 2:
                    xd = outline;
                    yd = -outline;

                    iz = z-0.1f;
                    ic = outline_colour;
                    break;
                  case 3:
                    xd = -outline;
                    yd = outline;

                    iz = z-0.1f;
                    ic = outline_colour;
                    break;
                  case 4:
                    xd = yd = outline;

                    iz = z-0.1f;
                    ic = outline_colour;
                    break;
                  case 5:
                    xd = yd = 0;

                    iz = z;
                    ic = colour;
                    break;
                }
                  
                pvr_prim(&font->hdr_array[font->pos_array[(int)string[n]]/font->txr_width], sizeof(pvr_poly_hdr_t)); 

                vert.flags = PVR_CMD_VERTEX;
                vert.x = x1+xd;
                vert.y = y1+yd;
                vert.z = iz;
                vert.u = u1;
                vert.v = 0;
                vert.argb = (((uint8)( fade * 255 ) ) << 24 ) | ic;

                vert.oargb = 0;
                pvr_prim(&vert, sizeof(vert));

                vert.x = x2+xd;
                vert.y = y2+yd;
                vert.u = u2;
                pvr_prim(&vert,sizeof(vert));

                vert.x = x3+xd;
                vert.y = y3+yd;
                vert.u = u1;
                vert.v = 1;
                pvr_prim(&vert,sizeof(vert));

                vert.flags = PVR_CMD_VERTEX_EOL;
                vert.x = x4+xd;
                vert.y = y4+yd;
                vert.u = u2;
                pvr_prim(&vert,sizeof(vert));
              }
            }
            
            cur_x+=wid;
          }
        }
        break;
    }

    n++;
  }

  /* ----------------------------------------------------------------- */
  /* Do these calculations now, so we can use them in the final test   */
  /* note that this means max_hgt is misnamed for a while, it's really */
  /* total height displayed after this calc.                           */
  /* ----------------------------------------------------------------- */

  max_wid = UTL_MAX((cur_x-x),max_wid);
  max_hgt = (cur_y+max_hgt)-y;

  if (display == 2)
  {
    pvr_vertex_t vert; 

    colour = 0xFFFFFF - colour;

    u1 = ((float)font->txr_width-7) / ((float)font->txr_width);
    u2 = ((float)font->txr_width-1) / ((float)font->txr_width);

    pvr_prim(&font->hdr_array[0], sizeof(pvr_poly_hdr_t)); 

    vert.flags = PVR_CMD_VERTEX;
    vert.x = x-1;
    vert.y = y-1;
    vert.z = z-0.1f;
    vert.u = u1;
    vert.v = 0;
    vert.argb = (((uint8)( fade * 255 ) ) << 24 ) | colour;

    vert.oargb = 0;
    pvr_prim(&vert, sizeof(vert));

    vert.x = x+max_wid+2;
    vert.u = u2;
    pvr_prim(&vert,sizeof(vert));

    vert.x = x-1;
    vert.y = y+max_hgt+2;
    vert.u = u1;
    vert.v = 8.0f/((float)font->txr_width);
    pvr_prim(&vert,sizeof(vert));

    vert.flags = PVR_CMD_VERTEX_EOL;
    vert.x = x+max_wid+2;
    vert.u = u2;
    pvr_prim(&vert,sizeof(vert));
  }

  if (wid_out)
    *wid_out = max_wid;

  if (hgt_out)
    *hgt_out = max_hgt;

  if (display < 0) // this indicates that auto centering requested
  {
    if (fc)
    {
      if (x == FNT_CENTRAL) x = (VID_SCREEN_W-max_wid)/2;
      if (y == FNT_CENTRAL) y = (VID_SCREEN_H-max_hgt)/2;
    }
    else
    {
      if (x == FNT_CENTRAL) x = (int)view_mid_x;
      if (y == FNT_CENTRAL) y = (int)view_mid_y;

      /* modify x and y positions */
      if (view_rot) 
      {
        float real_x = (float)x - view_mid_x;
        float real_y = (float)y - view_mid_y;
      
        x = (int32)(view_mid_x+((real_x*view_rcos)+(real_y*view_rsin)));
        y = (int32)(view_mid_y-((real_x*view_rsin)-(real_y*view_rcos)));
      }
  
      mid_x = x-view_x;
      mid_y = y-view_y;
  
      x = -(max_wid/2);
      y = -(max_hgt/2);
    }

    display = -display;

    goto REDO;
  }

  DBG_EXIT(99,("fnt_write")); 

  return;
}

/* fnt_render_obj ___________________________________________________________ */
/*                                                                            */
/* __________________________________________________________________________ */

int fnt_render_obj(uint32 info, 
                   uint32 unused,
                   int32 map_x, 
                   int32 map_y, 
                   float z_order,
                   int32 rotation, 
                   float scale, 
                   float fade,
                   uint32 colour)
{
  int rc = 1;

  if (info < FNT_MAX_OBJECTS)
  {
    fnt_obj_t *fnt_obj = &fnt_objs[info-1];

    fnt_render(fnt_first,
               fnt_obj->text,
               map_x, map_y, z_order, rotation,
               scale, fade, colour,
               fnt_obj->display,
               fnt_obj->outline_wid, 
               fnt_obj->outline_colour);
  }
  else
  {
    char scoretext[10];

    sprintf(scoretext,"%u",(unsigned int)unused);

    fnt_render(fnt_first,
               scoretext,
               map_x, map_y, z_order, rotation,
               scale, fade, colour,
               3, 3, 0x000000);
  }

  return rc;
}

/* fnt_render _______________________________________________________________ */
/*                                                                            */
/* __________________________________________________________________________ */

int fnt_render(fnt_info_t *font,
               char *text,
               int32 map_x, int32 map_y, float z_order, int rotation,
               float scale, float fade, uint32 colour,
               int8 display, int outline_wid, uint32 outline_colour)
{
  int rc = 1;

  DBG_ENTRY(99,("fnt_render(%p,%p,%d,%d,%f,%f,%f,0x%08X,%d,%d,0x%08X)",
                font, text, (int)map_x, (int)map_y, (double)z_order, (double)scale,
                (double)fade, (unsigned int)colour, display, outline_wid,
                (unsigned int)outline_colour));

  fnt_write(font,
            text,
            map_x, map_y, 0, z_order, rotation,
            scale*view_scale, fade, colour, 0, 0,
            -display, outline_wid, outline_colour);

  DBG_EXIT(99,("fnt_render rc=%d",rc));

  return rc;
}

/* fnt_obj_create_str _______________________________________________________ */
/*                                                                            */
/* Create a font object given an input string from a file                    */
/* __________________________________________________________________________ */

uint32 fnt_obj_create_str(char *string)
{
  uint32 info = 0;

  DBG_ENTRY(99,("fnt_obj_create_str(%s)",string));
 
  for(info=0;info<FNT_MAX_OBJECTS;info++)
    if (fnt_objs[info].text == 0)
      break;

  if (info<FNT_MAX_OBJECTS)
  {
    char *q1,*q2=0;

    if ((q1 = strchr(string,'\"')))
    {
      q1++;

      q2 = strchr(q1,'\"');
    }

    if (q2 && (fnt_objs[info].text = (char *)utl_malloc(q2-q1)))
    {
      char *t;

      strncpy(fnt_objs[info].text,q1,q2-q1);
      fnt_objs[info].text[q2-q1] = 0;

      if ((t = strtok(q2+1,UTL_WHITESPACE)))
        fnt_objs[info].display=(int8)strtol(t,0,0);
      if ((t = strtok(0, UTL_WHITESPACE)))
        fnt_objs[info].outline_wid=(int)strtol(t,0,0);
      if ((t = strtok(0, UTL_WHITESPACE)))
        fnt_objs[info].outline_colour=(uint32)strtol(t,0,0);

      info += 1;
    }
    else
      info = 0;
  }
  else
    info = 0;

  DBG_EXIT(99,("fnt_obj_create_str rc=0x%08X",(unsigned int)info));

  return info;
}

/* fnt_obj_free _____________________________________________________________ */
/*                                                                            */
/* __________________________________________________________________________ */

void fnt_obj_free(uint32 info)
{
  DBG_ENTRY(99,("fnt_obj_free(0x%08X)",(unsigned int)info));
  
  if (info)
  {
    info -= 1;

    utl_free(fnt_objs[info].text);
    fnt_objs[info].text = 0;
  }
  
  DBG_EXIT(99,("fnt_obj_free"));
}

/* fnt_inst_create __________________________________________________________ */
/*                                                                            */
/* __________________________________________________________________________ */

uint32 fnt_inst_create(uint32 info)
{
  uint32 inst = 0;
  
  DBG_ENTRY(99,("fnt_inst_create(0x%08X)",(unsigned int)info));
  
  DBG_EXIT(99,("fnt_inst_create rc=%d",(int)inst));
  
  return inst;
}

/* fnt_inst_free ____________________________________________________________ */
/*                                                                            */
/* __________________________________________________________________________ */

void fnt_inst_free(uint32 info)
{
  DBG_ENTRY(99,("fnt_inst_free(0x%08X)",(unsigned int)info));
  
  DBG_EXIT(99,("fnt_inst_free"));
}

/* fnt_load _________________________________________________________________ */
/*                                                                            */
/* Load the named font files into the font structure.                         */
/* __________________________________________________________________________ */

int fnt_load(char *font_name, fnt_info_t *font)
{
  int loaded = 0;

  DBG_ENTRY(99,("fnt_load(%s,%p)",font_name,font));
  
  if (font_name && font)
  {
    FILE *fp = 0;

    char name[256];

    sprintf(name,"%s/gfx/%s.%s",data_dir,font_name,FNT_DEF_FILETYPE);

    if ((fp = fopen(name,"rb")))
    {
      char *t;
      char line[1024];
    
      /* Read in the font header */
      if (fgets(line,sizeof(line),fp))
      {
        memset(font,0x00,sizeof(*font));

        if ((t=strtok(line,UTL_WHITESPACE))) font->txr_width = strtol(t,0,10);
        if ((t=strtok(0,UTL_WHITESPACE))) font->txr_height = strtol(t,0,10);
        if ((t=strtok(0,UTL_WHITESPACE))) font->height = strtol(t,0,10);

        while(loaded >= 0 && (t=strtok(0,UTL_WHITESPACE)))
        {
          sprintf(name,"%s/gfx/%s",data_dir,t);

          DBG_PRINT(99,("Loading graphics '%s'",name));
            
          /* use png_load_texture to load the actual font graphics*/
          if (0 == png_load_texture(name,
                                    &font->txr_array[loaded],
                                    PNG_FULL_ALPHA,
                                    &font->txr_width,
                                    &font->txr_height))
          {
            pvr_poly_cxt_t cxt;

            pvr_poly_cxt_txr(&cxt, 
                             PVR_LIST_TR_POLY, 
                             PVR_TXRFMT_ARGB4444, 
                             font->txr_width, 
                             font->txr_height, 
                             font->txr_array[loaded], 
                             PVR_FILTER_BILINEAR);

            pvr_poly_compile(&font->hdr_array[loaded], 
                             &cxt);

            loaded++;
          }
          else
          {
            DBG_PRINT(99,("png_load_texture failed"));
            
            /* flag that the overall loading of the font failed */
            loaded = -1 * loaded;
          }
        }
      }
      
      if (loaded > 0)
      {
        int n = 0;
        
        if ((font->pos_array = (int*)utl_malloc(256*sizeof(int))))
        {
          while(n<256 && fgets(line,sizeof(line),fp))
          {
            t = strtok(line,UTL_WHITESPACE);
            
            while(t)
            {
              font->pos_array[n++] = strtol(t,0,10);
              t = strtok(0,UTL_WHITESPACE);
            }
          }
        }
        else
          loaded = -1 * loaded;
      }

      /* ----------------------------------- */
      /* clear up any partially loaded fonts */
      /* ----------------------------------- */

      if (loaded < 0)
      {
        int i;

        loaded = -1 * loaded;

        for(i=0;i<loaded;i++)
          pvr_mem_free(font->txr_array[i]);
      }

      fclose(fp);
    }
    else
      loaded = 0;
  }
  else
    loaded = 0;

  if (loaded && fnt_first == 0)
    fnt_first = font;

  DBG_EXIT(99,("fnt_load rc=%d",loaded));

  return(loaded);
}

/* fnt_unload _______________________________________________________________ */
/*                                                                            */
/* Unload / free resources for given font.                                    */
/* __________________________________________________________________________ */

void fnt_unload(fnt_info_t *font)
{
  int n;

  DBG_ENTRY(99,("fnt_unload(%p)",font));
  
  for(n=0;n<FNT_MAX_IMAGES;n++)
  {
    pvr_mem_free(font->txr_array[n]);
    font->txr_array[n] = NULL;
  }

  utl_free(font->pos_array);
  font->pos_array = NULL;
  
  DBG_EXIT(99,("fnt_unload"));
  
  return;
}

#undef FNT_C
